﻿// -------------------------------------------------------------------------
//    @FileName         :    NFCityMapModule.cpp
//    @Author           :    Gao.Yi
//    @Date             :    2025-09-15
//    @Email			:    445267987@qq.com
//    @Module           :    NFCityMapModule
//    @Desc             :
//
// -------------------------------------------------------------------------

#include "NFCityMapModule.h"

#include "NFCityMapInst.h"
#include "NFCityMapMgr.h"
#include "NFCityMapUtil.h"
#include "NFCityPacketSendUtil.h"
#include "NFCitySession.h"
#include "proto_svr_common.nanopb.h"
#include "proto_svr_map.nanopb.h"
#include "DescStore/CityMapDesc.h"
#include "NFCityServer/NFCityServerPlayerPlugin/Player/NFCityPlayer.h"
#include "NFCityServer/NFCityServerPlayerPlugin/Player/NFCityPlayerUtil.h"
#include "NFServerComm/NFServerCommon/NFIServerMessageModule.h"

NFCityMapModule::NFCityMapModule(NFIPluginManager* p) : NFCityDynamicModule(p)
{
}

NFCityMapModule::~NFCityMapModule()
{
}

int NFCityMapModule::Awake()
{
    RegisterServerMessage(NF_ST_CITY_SERVER, E_GC_CITYSVR_MAPMSG_REQ);
    RegisterClientMessage(NF_ST_CITY_SERVER, E_CS_MAP_ENTER_REQ);
    RegisterClientMessage(NF_ST_CITY_SERVER, E_CS_MAP_POS_REQ);
    RegisterClientMessage(NF_ST_CITY_SERVER, E_CS_MAP_ACTION_REQ);
    RegisterClientMessage(NF_ST_CITY_SERVER, E_CS_MAP_LEAVE_REQ);
    return 0;
}

int NFCityMapModule::OnHandleServerMessage(uint64_t unLinkId, NFDataPackage& packet)
{
    if (!m_pObjPluginManager->IsInited(NF_ST_CITY_SERVER))
    {
        NFLogError(NF_LOG_DEFAULT, packet.nParam1, "City Server not inited, drop client msg:{}", packet.ToString());
        return -1;
    }

    if (m_pObjPluginManager->IsServerStopping())
    {
        NFLogError(NF_LOG_DEFAULT, packet.nParam1, "City Server is Stopping, drop client msg:{}", packet.ToString());
        return -1;
    }

    switch (packet.nMsgId)
    {
        case E_GC_CITYSVR_MAPMSG_REQ:
        {
            OnHandleGCMapMsgReq(unLinkId, packet);
            break;
        }
        default:
        {
            NFLogError(NF_LOG_DEFAULT, 0, "Server MsgId:{} Register, But Not Handle, Package:{}", packet.nMsgId, packet.ToString());
            break;
        }
    }
    return 0;
}

int NFCityMapModule::OnHandleGCMapMsgReq(int64_t unLinkId, NFDataPackage& packet)
{
    proto_ff::Proto_GCMapMsgReq req;
    CLIENT_MSG_PROCESS_WITH_PRINTF(packet, req);

    switch (req.req_data().type())
    {
        case EN_MAP_MSG_ENTER_MAP:
        {
            OnHandleGCEnterMapReq(packet.nSrcId, packet.nParam1, req);
            break;
        }
        default:
        {
            LOG_ERR(0, -1, "not support map msg type:{}", req.req_data().type());
            break;
        }
    }
    return 0;
}

int NFCityMapModule::OnHandleGCEnterMapReq(int iSvrId, int transId, const proto_ff::Proto_GCMapMsgReq& req)
{
    int iRetCode = 0;
    auto pConfig = FindModule<NFIConfigModule>()->GetAppConfig(NF_ST_CITY_SERVER);
    CHECK_NULL(0, pConfig);

    const proto_ff::GCMapMsgReqData& oReqData = req.req_data();
    int iEnterMapId = oReqData.enter_map().map_id();
    uint64_t roleId = req.role_id();

    auto pMapDesc = CityMapDesc::Instance()->GetDesc(iEnterMapId);
    CHECK_NULL_WF(roleId, pMapDesc, "map desc not exist:{}", iEnterMapId);


    auto pPlayer = NFCityPlayerUtil::GetPlayerByRoleID(roleId);
    if (pPlayer)
    {
        pPlayer->InitRoleInfo(&oReqData.enter_map().role_info());
    }
    else
    {
        pPlayer = NFCityPlayerUtil::CreatePlayer(roleId);
        CHECK_NULL_WF(roleId, pPlayer, "create player failed:{}", roleId);

        pPlayer->InitRoleInfo(&oReqData.enter_map().role_info());
    }
    pPlayer->SetLogicSvrId(iSvrId);

    //enter map
    auto pEnterMapInst = NFCityMapMgr::Instance()->GetMapInst(pPlayer, iEnterMapId);
    CHECK_NULL_WF(pPlayer->GetRoleID(), pEnterMapInst, "get map inst failed! mapId:{}", iEnterMapId);

    auto pRoleMapData = pPlayer->GetMoveExt()->GetMapData();
    CHECK_NULL(pPlayer->GetRoleID(), pRoleMapData);
    int iDirection = 0;
    Proto_Vector3 oEnterPos;
    pEnterMapInst->GetEnterPos(oEnterPos, iDirection);

    if (req.cs_req().map_query_req().use_point())
    {
        oEnterPos.x = req.cs_req().map_query_req().x_point();
        oEnterPos.y = req.cs_req().map_query_req().y_point();
        oEnterPos.z = req.cs_req().map_query_req().z_point();
    }

    if (pRoleMapData->map_desc_id == iEnterMapId)
    {
        iDirection = pRoleMapData->direction;
        oEnterPos = pRoleMapData->pos;
    }

    if (!pEnterMapInst->IsPositionValid(oEnterPos))
    {
        pEnterMapInst->GetEnterPos(oEnterPos, iDirection);
    }

    if (pPlayer->GetMapInstId() == pEnterMapInst->GetGlobalId())
    {
        //already in map
        //NFCityMapUtil::RefreshNeighborView(pPlayer, pEnterMapInst, oEnterPos);
        //NFCityMapUtil::RefreshPlayerView(pPlayer);
    }
    else
    {
        iRetCode = NFCityMapUtil::EnterMap(pPlayer, pEnterMapInst, oEnterPos, iDirection, true);
        CHECK_ERR(pPlayer->GetRoleID(), iRetCode, "enter map id:{} failed!", iEnterMapId);
    }

    pPlayer->GetMoveExt()->ClearLastAction();
    pPlayer->GetDanceExt()->StopDance(pPlayer);
    pPlayer->SetEnterSvr(false);

    proto_ff::Proto_GCMapMsgRsp gcCMapMsgRsp;
    gcCMapMsgRsp.set_role_id(roleId);
    gcCMapMsgRsp.set_result(0);
    gcCMapMsgRsp.set_role_obj_id(pPlayer->GetGlobalId());
    proto_ff::GCMapMsgRspData* pRspData = gcCMapMsgRsp.mutable_rsp_data();
    CHECK_NULL(0, pRspData);

    pRspData->set_type(proto_ff::EN_MAP_MSG_ENTER_MAP);
    pPlayer->GetMoveExt()->GetMapData()->ToPb(pRspData->mutable_map_data());
    pRspData->set_map_obj_id(pEnterMapInst->GetGlobalId());
    pRspData->set_token(pPlayer->GetToken());

    std::vector<NF_SHARE_PTR<NFServerData>> vecAll = FindModule<NFIMessageModule>()->GetAllServer(NF_ST_CITY_SERVER, NF_ST_PROXY_SERVER);
    for (int i = 0; i < (int)vecAll.size(); ++i)
    {
        auto pServerData = vecAll[i];
        if (pServerData && pServerData->mServerInfo.route_server_type() == NF_ST_CITY_SERVER && NFServerIDUtil::GetBusID(pServerData->mServerInfo.route_agent()) == pConfig->GetBusId())
        {
            pRspData->set_ip(NFStringUtility::IpToNum(pServerData->mServerInfo.external_server_ip().c_str()));
            pRspData->set_port(pServerData->mServerInfo.external_server_port());
            pRspData->set_url(pServerData->mServerInfo.external_server_ip());
            break;
        }
    }

    gcCMapMsgRsp.set_cmd(req.cmd());
    gcCMapMsgRsp.mutable_cs_req()->CopyFrom(req.cs_req());

    FindModule<NFIServerMessageModule>()->SendTransToLogicServer(NF_ST_CITY_SERVER, iSvrId, E_GC_CITYSVR_MAPMSG_RSP, gcCMapMsgRsp, roleId, 0, transId);
    return 0;
}

int NFCityMapModule::OnHandleClientMessage(NFDataPackage& packet, NFCitySession* pSession, const proto_ff::Proto_CSReq& req)
{
    if (!m_pObjPluginManager->IsInited(NF_ST_CITY_SERVER))
    {
        NFLogError(NF_LOG_DEFAULT, packet.nParam1, "City Server not inited, drop client msg:{}", packet.ToString());
        return -1;
    }

    if (m_pObjPluginManager->IsServerStopping())
    {
        NFLogError(NF_LOG_DEFAULT, packet.nParam1, "City Server is Stopping, drop client msg:{}", packet.ToString());
        return -1;
    }

    int iRetCode = 0;
    switch (packet.nMsgId)
    {
        case E_CS_MAP_ENTER_REQ:
        {
            iRetCode = OnHandleMapEnterReq(packet, pSession, req);
            CHECK_ERR(0, iRetCode, "handle map enter req failed:{}", packet.ToString());
            break;
        }
        case E_CS_MAP_LEAVE_REQ:
        {
            iRetCode = OnHandleMapLeaveReq(packet, pSession, req);
            CHECK_ERR(0, iRetCode, "handle map leave req failed:{}", packet.ToString());
            break;
        }
        case E_CS_MAP_POS_REQ:
        {
            iRetCode = OnHandleMapPosReq(packet, pSession, req);
            CHECK_ERR(0, iRetCode, "handle map enter pos failed:{}", packet.ToString());
            break;
        }
        case E_CS_MAP_ACTION_REQ:
        {
            iRetCode = OnHandleMapActionReq(packet, pSession, req);
            CHECK_ERR(0, iRetCode, "handle map action req failed:{}", packet.ToString());
            break;
        }
        default:
        {
            NFLogError(NF_LOG_DEFAULT, 0, "Server MsgId:{} Register, But Not Handle, Package:{}", packet.nMsgId, packet.ToString());
            iRetCode = -1;
            break;
        }
    }
    return iRetCode;
}

int NFCityMapModule::OnHandleMapEnterReq(NFDataPackage& packet, NFCitySession* pSession, const proto_ff::Proto_CSReq& req)
{
    CHECK_NULL(0, pSession);
    NFCityPlayer* pPlayer = pSession->GetPlayer();
    CHECK_NULL(pSession->GetRoleId(), pPlayer);

    const proto_ff::Proto_CSMapEnterReq& oMapEnterReq = req.map_enter_req();

    NFCityMapInst* pEnterMapInst = NFCityMapMgr::Instance()->GetMapInst(pPlayer, oMapEnterReq.map_desc_id());
    CHECK_NULL_WF(pPlayer->GetRoleID(), pEnterMapInst, "map inst not exist:{}", oMapEnterReq.map_desc_id());

    if (!pPlayer->IsEnterMap())
    {
        LOG_ERR(pPlayer->GetRoleID(), -1, "player not enter map, role_id:{}", pPlayer->GetRoleID());
        return -1;
    }

    pPlayer->GetMoveExt()->ClearLastAction();
    pPlayer->SetStateLeave(false);

    //notify enter map rsp
    NFCityMapUtil::NotifyMapEnterRsp(pPlayer);

    //enter view
    pPlayer->SetEnterSvr(true);

    NFCityNeighborGrid oGrids;
    pEnterMapInst->GetNeighborGrids(pPlayer, oGrids);

    proto_ff::Proto_CSRsp oCSRsp;
    NFMakeNpcView oMakeNpcView(pPlayer, &oCSRsp);
    oGrids.IterateGrids(&oMakeNpcView, ECITYWLT_NPC);

    NFMakePlayerView oMakePlayerView(pPlayer, &oCSRsp);
    pPlayer->GetViewList()->IteratorViewList(&oMakePlayerView);
    oMakePlayerView.Send();

    return 0;
}

int NFCityMapModule::OnHandleMapLeaveReq(NFDataPackage& packet, NFCitySession* pSession, const proto_ff::Proto_CSReq& req)
{
    CHECK_NULL(0, pSession);
    NFCityPlayer* pPlayer = pSession->GetPlayer();
    CHECK_NULL(pSession->GetRoleId(), pPlayer);

    pPlayer->SetEnterSvr(false);

    proto_ff::Proto_CSRsp oCSRsp;
    proto_ff::Proto_CSMapLeaveRsp* pMapLeaveRsp = oCSRsp.mutable_map_leave_rsp();
    CHECK_NULL(0, pMapLeaveRsp);

    pMapLeaveRsp->set_result(0);

    NFCityPacketSendUtil::SendToClient(pPlayer, proto_ff::E_CS_MAP_LEAVE_RSP, &oCSRsp);

    LOGSVR_TRACE("--end--");

    return 0;
}

int NFCityMapModule::OnHandleMapPosReq(NFDataPackage& packet, NFCitySession* pSession, const proto_ff::Proto_CSReq& req)
{
    int iRetCode = 0;
    CHECK_NULL(0, pSession);
    NFCityPlayer* pPlayer = pSession->GetPlayer();
    CHECK_NULL(pSession->GetRoleId(), pPlayer);

    const proto_ff::Proto_CSActorPosReq& oActorPosReq = req.map_pos_req().actor_pos_req();

    NFCityWorldObject* pActionWo = NFCityWorldObject::GetObjByGlobalId(oActorPosReq.id(), true);
    CHECK_NULL_WF(pPlayer->GetRoleID(), pActionWo, "action obj not exist:{}", oActorPosReq.id());

    NFCityMapInst* pMapInst = NFCityMapInst::GetObjByGlobalId(pActionWo->GetMapInstId());
    CHECK_NULL_WF(pPlayer->GetRoleID(), pMapInst, "map inst not exist:{}", pActionWo->GetMapInstId());

    //check
    iRetCode = CheckActionObject(pPlayer, pActionWo, 0);
    if (iRetCode)
    {
        LOGSVR_ERROR("Check Action Object Failed Ret:" << iRetCode);
        return 0;
    }

    //set pos
    Proto_Vector3 oEndPos;
    oEndPos.FromPb(oActorPosReq.pos());

    if (!pMapInst->IsPositionValid(oEndPos))
    {
        LOGSVR_TRACE("invalid position x:" << oEndPos.x << " z:" << oEndPos.z);
        return 0;
    }

    if (pPlayer->IsCheckPos())
    {
        Proto_Vector3* pActorPos = pActionWo->GetPosition();
        int64_t ullXDis = oEndPos.x - pActorPos->x;
        int64_t ullZDis = oEndPos.z - pActorPos->z;
        uint64_t ullDis2 = ullXDis * ullXDis + ullZDis * ullZDis;
        uint64_t ullCheckDis2 = (DEFAULT_ACTOR_SPEED + 30000) * (DEFAULT_ACTOR_SPEED + 30000);
        if (ullDis2 > ullCheckDis2)
        {
            LOG_ERR(pPlayer->GetRoleID(), -1, "actor speed up....");
        }
    }

    proto_ff::Proto_CSRsp oCSRsp;
    NFCityPacketSendUtil::SendToClient(pPlayer, proto_ff::E_CS_MAP_POS_RSP, &oCSRsp);

    pMapInst->MovePos(pActionWo, oEndPos, oActorPosReq.dir());

    return iRetCode;
}

int NFCityMapModule::OnHandleMapActionReq(NFDataPackage& packet, NFCitySession* pSession, const proto_ff::Proto_CSReq& req)
{
    int iRetCode = 0;
    auto pPlayer = pSession->GetPlayer();
    CHECK_NULL(0, pPlayer);

    const proto_ff::Proto_CSMapActionReq& oMapActionReq = req.map_action_req();
    const proto_ff::Proto_PvpSynAction& oAction = oMapActionReq.action();

    NFCityWorldObject* pActionWo = NFCityWorldObject::GetObjByGlobalId(oMapActionReq.obj_id(), true);
    CHECK_NULL_WF(pPlayer->GetRoleID(), pActionWo, "action obj not exist:{}", oMapActionReq.obj_id());

    NFCityMapInst* pMapInst = NFCityMapInst::GetObjByGlobalId(pActionWo->GetMapInstId());
    CHECK_NULL_WF(pPlayer->GetRoleID(), pMapInst, "map inst not exist:{}", pActionWo->GetMapInstId());

    //check
    iRetCode = CheckActionObject(pPlayer, pActionWo, oAction.roleid());
    if (iRetCode != 0)
    {
        return 0;
    }

    //set pos
    Proto_Vector3 oEndPos;
    oEndPos.FromPb(oAction.position());

    if (!pMapInst->IsPositionValid(oEndPos))
    {
        LOGSVR_TRACE("invalid position x:" << oEndPos.x << " z:" << oEndPos.z);
        return 0;
    }

    proto_ff::Proto_CSRsp oCSRsp;
    NFCityPacketSendUtil::SendToClient(pPlayer, proto_ff::E_CS_MAP_ACTION_RSP, &oCSRsp);

    pMapInst->MovePos(pActionWo, oEndPos, oAction.direction());
    if (pActionWo->IsPlayer())
    {
        NFCityPlayer* pActionPlayer = dynamic_cast<NFCityPlayer*>(pActionWo);
        CHECK_NULL(pPlayer->GetRoleID(), pActionPlayer);

        if (oAction.emoji_id() == -1)
        {
            pActionPlayer->GetMoveExt()->SetDance(false);
            pActionPlayer->GetMoveExt()->SetLastAction(&oAction);
        }
        else if (oAction.emoji_id() == DEFAULT_DANCE_EMOJI_ID)
        {
            pActionPlayer->GetMoveExt()->SetDanceAction(&oAction);
        }
        else
        {
            pActionPlayer->GetMoveExt()->SetDance(false);
        }

        //syn
        proto_ff::Proto_CSRsp oCSNtf;
        proto_ff::Proto_CSMapActionNtf* pMapActionNtf = oCSNtf.mutable_map_action_ntf();
        proto_ff::Proto_PvpSynAction* pActionNtf = pMapActionNtf->mutable_action();
        oCSNtf.mutable_map_action_ntf()->set_obj_id(oMapActionReq.obj_id());
        pActionNtf->CopyFrom(oAction);

        NFCityMapUtil::BroadCastViewNeighbor(pActionPlayer, proto_ff::E_CS_MAP_ACTION_NTF, &oCSNtf);

        if (pActionPlayer != pPlayer)
        {
            NFCityPacketSendUtil::SendToClient(pActionPlayer, proto_ff::E_CS_MAP_ACTION_NTF, &oCSNtf);
        }
    }

    //handle dance
    if (oAction.emoji_id() == DEFAULT_DANCE_EMOJI_ID)
    {
        pPlayer->GetDanceExt()->StartDance(pPlayer);
    }
    else
    {
        pPlayer->GetDanceExt()->StopDance(pPlayer);
    }

    return iRetCode;
}


int NFCityMapModule::CheckActionObject(NFCityPlayer* pPlayer, NFCityWorldObject* pCtrlWo, uint64_t ullRoleId)
{
    CHECK_NULL(0, pPlayer);
    CHECK_NULL(0, pCtrlWo);

    CHECK_EXPR(pPlayer->IsEnterSvr(), INVALID_ID, "player is not enter server");

    if (pCtrlWo->IsPlayer())
    {
        NFCityPlayer* pCtrlPlayer = dynamic_cast<NFCityPlayer*>(pCtrlWo);
        CHECK_NULL(0, pCtrlPlayer);
        if (pCtrlPlayer == pPlayer)
        {
            auto pTeamInfo = pCtrlPlayer->GetTeamExt();
            if (!pTeamInfo->IsTeamLeader(pCtrlPlayer->GetRoleID()) && pTeamInfo->IsTeamFollow(pCtrlPlayer->GetRoleID()))
            {
                return INVALID_ID;
            }

            return 0;
        }

        //team follow
        auto pPlayerTeam = pCtrlPlayer->GetTeamExt();
        if (pPlayerTeam->IsTeamLeader(pPlayer->GetRoleID()) && pPlayerTeam->IsTeamFollow(pCtrlPlayer->GetRoleID()))
        {
            if (pPlayer->GetMapInstId() == pCtrlPlayer->GetMapInstId())
            {
                return 0;
            }
        }

        CHECK_EXPR((pCtrlPlayer->GetRoleID() == ullRoleId || ullRoleId == 0), -1, "invalid roleid");
    }

    return INVALID_ID;
}
